'use strict'
const axios = require('axios')

const Service = require('egg').Service

const device_message = require('../../models/device_message')
const EquipmentModel = require('../../models/equipment')
const main_fence_table = require('../../models/main_fence_table')
const fence_sub_table = require('../../models/fence_sub_table')
const trajectory_table = require('../../models/trajectory_table')
const user_bound_device = require('../../models/user_bound_device')
const dayjs = require('dayjs')
const user = require('../../models/user')

// 创建一个雪花算法生成器实例
const Snowflake = require('snowflake-util')
const snowflake = new Snowflake({ id: 1n })

class TrackUploadService extends Service {
  //设备
  equipmentModel = EquipmentModel(this.app.model)
  //设备消息
  deviceMessage = device_message(this.app.model)
  //围栏主
  mainFenceTtable = main_fence_table(this.app.model)
  //围栏子
  fenceSubTable = fence_sub_table(this.app.model)
  //轨迹数据
  trajectoryTable = trajectory_table(this.app.model)
  //用户绑定设备
  userBoundDevice = user_bound_device(this.app.model)
  //用户
  userModel = user(this.app.model)

  async generateId() {
    return snowflake.generate().toString()
  }

  async query() {
    const { ctx } = this
    const { id: equipment_id } = ctx.query
    const where = { del_flag: '0', equipment_id }
    try {
      const result = await this.trajectoryTable.findOne({
        where,
        attributes: ['longitude', 'dimension'],
        order: [['submit_date', 'ASC']],
      })
      if (result) {
        ctx.success('成功', result)
      } else {
        ctx.success('未查询到设备数据数据')
      }
    } catch (error) {
      return ctx.failure('系统异常', error)
    }
  }

  /**
   * // 示例
   * const point = { x: 3, y: 3 };
   * const polygon = [
   *  { x: 0, y: 0 },
   *  { x: 5, y: 0 },
   *  { x: 5, y: 5 },
   *  { x: 0, y: 5 }
   * ];
   *
   * console.log(isPointInPolygon(point, polygon));
   * 判断点是否在多边形内
   * @param {Object} point - 点的坐标 { x: number, y: number }
   * @param {Array} polygon - 多边形顶点数组 [{ x: number, y: number }, ...]
   * @returns {boolean} - 点是否在多边形内
   */
  async isPointInPolygon(point, polygon) {
    let { x, y } = point
    let isInside = false

    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
      const xi = polygon[i].x,
        yi = polygon[i].y
      const xj = polygon[j].x,
        yj = polygon[j].y

      // 判断点是否在边的两端之间，并且在射线的上方或下方
      const intersect =
        yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
      if (intersect) {
        isInside = !isInside
      }
    }

    return isInside
  }

  //坐标转换
  /**
   * 将度分格式的坐标转换为十进制度数
   * @param {string} coord - 度分格式的坐标，例如 "11353.46949E" 或 "2233.23426N"
   * @returns {number} - 转换后的十进制度数
   */
  async convertDMToDecimal(dmString) {
    // 提取方向（最后一个字符）
    let direction = dmString.slice(-1)

    // 去掉方向，转换为数值
    let dmValue = parseFloat(dmString.slice(0, -1))

    // 解析度（DD）和分（MM.MMMM）
    let degrees = Math.floor(dmValue / 100)
    let minutes = dmValue % 100

    // 计算十进制坐标
    let decimal = degrees + minutes / 60

    // 如果是西经(W)或南纬(S)，需要变为负数
    if (direction === 'W' || direction === 'S') {
      decimal = -decimal
    }

    return decimal
  }

  async dataProcessing(Longitude, Latitude, imei) {
    if (!Longitude || !Latitude || !imei) {
      console.log('缺少必要的参数')
      return
    }

    // 查询设备ID
    const equipment = await this.equipmentModel.findAll({
      where: {
        equipment_id: imei,
        del_flag: 0,
      },
    })

    // 经纬度转换为十进制度数
    Longitude = await this.convertDMToDecimal(Longitude)
    Latitude = await this.convertDMToDecimal(Latitude)

    // 判断设备是否存在
    if (equipment.length === 0) {
      console.log('设备不存在')
      return
    }

    // 查询设备关联的所有围栏
    const enclosure = await this.mainFenceTtable.findAll({
      where: {
        equipment_id: equipment[0].id,
        del_flag: 0,
      },
    })

    // 判断设备是否关联围栏, 设备未关联围栏时直接落库定位数据
    if (enclosure.length === 0) {
      await this.trajectoryTable.create({
        id: await this.generateId(),
        equipment_id: equipment[0].id,
        longitude: Longitude,
        dimension: Latitude,
        submit_date: new Date(),
      })
      return
    }

    // 设备关联围栏时, 判断定位数据是否在某个围栏内
    for (let i = 0; i < enclosure.length; i++) {
      const mainFenceId = enclosure[i].id
      const subFence = await this.fenceSubTable.findAll({
        where: {
          main_id: mainFenceId,
          del_flag: 0,
        },
      })

      if (subFence.length === 0) {
        continue
      }

      const polygon = subFence.map(fence => ({
        x: fence.longitude,
        y: fence.dimension,
      }))

      if (await this.isPointInPolygon({ x: Longitude, y: Latitude }, polygon)) {
        const isInside = await this.deviceMessage.findAll({
          where: {
            equipment_id: equipment[0].id,
            fence_id: mainFenceId,
            type: 1,
          },
        })

        if (isInside.length !== 0) {
          return
        }

        const userBoundDevice = await this.userBoundDevice.findAll({
          where: {
            equipment_id: equipment[0].id,
          },
        })

        if (userBoundDevice.length === 0) {
          return
        }

        const user = await this.userModel.findAll({
          where: {
            id: userBoundDevice[0].user_id,
          },
        })

        if (user.length === 0) {
          return
        }

        await this.deviceMessage.create({
          id: await this.generateId(),
          equipment_id: equipment[0].id,
          fence_id: mainFenceId,
          type: 1,
        })
        await this.messageSending(
          user[0],
          enclosure[i].name,
          '进入',
          equipment[0]
        )
        return
      }
    }

    const isInside = await this.deviceMessage.findAll({
      where: {
        equipment_id: equipment[0].id,
        type: 1,
      },
    })

    if (isInside.length === 0) {
      return
    }

    const userBoundDevice = await this.userBoundDevice.findAll({
      where: {
        equipment_id: equipment[0].id,
      },
    })

    if (userBoundDevice.length === 0) {
      return
    }

    const user = await this.userModel.findAll({
      where: {
        id: userBoundDevice[0].user_id,
      },
    })

    if (user.length === 0) {
      return
    }

    await this.messageSending(user[0], enclosure[i].name, '离开', equipment[0])
    await this.deviceMessage.update(
      { del_flag: 1 },
      {
        where: {
          id: isInside[0].id,
        },
      }
    )

    await this.trajectoryTable.create({
      id: await this.generateId(),
      equipment_id: equipment[0].id,
      longitude: Longitude,
      dimension: Latitude,
      submit_date: new Date(),
    })
  }

  async messageSending(user, name, action, equipment) {
    const url =
      'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET'
    const response = await axios.get(url)
    const access_token = response.data.access_token
    const sendUrl = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${access_token}`

    const data = {
      touser: user.open_id,
      template_id: 'yIHhlkFMy68LGbCs3SOA-p6U4PBZHurDXDYLE5KGjts',
      page:
        'ind/pages/track/track?equipment_id=' +
        equipment.id +
        '&start_time=' +
        dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss') +
        '&end_time=' +
        dayjs(new Date()).subtract(10, 'minute').format('YYYY-MM-DD HH:mm:ss'),
      data: {
        thing31: {
          value: name,
        },
        phrase18: {
          value: action,
        },
        name3: {
          value: equipment.name,
        },
        time5: {
          value: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
        },
      },
    }
    const sendResponse = await axios.post(sendUrl, data)
    console.log(sendResponse)
  }
}

module.exports = TrackUploadService
